Skip to content

feat(tui): add /provider command, custom registry import, and tabbed model selector#264

Open
sailist wants to merge 2 commits into
MoonshotAI:mainfrom
sailist:haozhe/provider-tui
Open

feat(tui): add /provider command, custom registry import, and tabbed model selector#264
sailist wants to merge 2 commits into
MoonshotAI:mainfrom
sailist:haozhe/provider-tui

Conversation

@sailist
Copy link
Copy Markdown
Collaborator

@sailist sailist commented Jun 1, 2026

Summary

This PR introduces a comprehensive provider/model management system for the TUI, including a new /provider slash command, a tabbed model selector, custom registry import support, and supporting UI dialogs. It also adds corresponding documentation updates and unit tests.


1. Provider & Model Management in TUI

Problem: Users previously had limited ability to manage LLM providers and select models directly from the TUI. Switching providers or importing custom registries required manual configuration file edits.

What was done:

  • Added a new /provider TUI slash command for managing providers interactively.
  • Implemented ProviderManagerDialog to list, add, edit, and remove providers.
  • Implemented TabbedModelSelector to let users browse and select models across providers in a tabbed interface.
  • Updated command dispatch and config wiring to integrate the new flows.

2. Custom Registry Import

Problem: There was no built-in way to import external provider registries (e.g., custom OpenAI-compatible endpoints) without manually editing configuration files.

What was done:

  • Added CustomRegistryImporter UI dialog for importing registries via URL or pasted JSON.
  • Added packages/oauth/src/custom-registry.ts module to parse, validate, and merge custom registry definitions.
  • Added comprehensive unit tests in packages/oauth/test/custom-registry.test.ts.

3. Documentation & Test Updates

Problem: New TUI commands and registry features lacked user-facing documentation, and existing tests needed updates for the changed command surface.

What was done:

  • Updated English and Chinese docs for provider configuration and slash-commands reference.
  • Updated existing TUI tests (choice-picker.test.ts, kimi-tui-message-flow.test.ts) to reflect new command additions.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked the related issue, if any.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have run make gen-changelog to update the changelog.
  • I have run make gen-docs to update the user documentation.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 1, 2026

🦋 Changeset detected

Latest commit: f9c847b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@moonshot-ai/kimi-code Minor
@moonshot-ai/kimi-code-oauth Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 1, 2026

pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@f9c847b
npx https://pkg.pr.new/@moonshot-ai/kimi-code@f9c847b

commit: f9c847b

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2d7290cd4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

baseUrl,
apiKey,
models,
selectedModelId: '', // no default yet; user picks in the model selector
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid writing an empty default model

When adding a known provider, this passes an empty selectedModelId into applyCatalogProvider, but that helper always sets config.defaultModel to ${providerId}/${selectedModelId}. If the user presses Esc in the following selector (which the comment says should leave the provider without a default selection), the config has already been saved with an invalid default such as anthropic/, so later config refreshes/startup cannot resolve the default model. Persist the provider/models without setting defaultModel, or clear it before setConfig when no model was selected.

Useful? React with 👍 / 👎.

Comment on lines +287 to +291
applyCustomRegistryProvider(
config as Parameters<typeof applyCustomRegistryProvider>[0],
entry as Parameters<typeof applyCustomRegistryProvider>[1],
source,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the provider before re-importing registry models

For an already-imported custom registry provider, applyCustomRegistryProvider deletes stale aliases only from this in-memory config, but host.harness.setConfig deep-merges patches, so aliases omitted from config['models'] are not removed from the existing TOML. Re-importing an api.json after a model is removed therefore leaves the old alias selectable, unlike the catalog path above which calls removeProvider before applying. Delete each existing provider id before applying its refreshed registry entry, or use a write path that can remove missing model keys.

Useful? React with 👍 / 👎.

type: entry.type,
baseUrl: entry.api,
apiKey: source.apiKey,
source,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist the registry source through config validation

Storing source here does not survive the actual setConfig path: provider patches are parsed by ProviderConfigSchema, which only keeps type, apiKey, baseUrl, defaultModel, oauth, env, and customHeaders. After import/reload, ProviderManagerComponent can no longer see cfg.source.kind === 'apiJson', so multiple providers from the same api.json are shown and deleted as unrelated standalone providers instead of one registry source. Add source to the managed provider schema/TOML handling or store the registry metadata in a supported field.

Useful? React with 👍 / 👎.

// only `adaptive_thinking = true`. Without this it would render as "unsupported"
// and switching to it would force thinking off.
if (caps.includes('thinking') || model.adaptiveThinking === true) return 'toggle';
if (caps.includes('thinking')) return 'toggle';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve adaptive-thinking aliases as toggleable

For aliases configured with adaptiveThinking: true but no explicit thinking capability, this now reports thinking as unsupported and effectiveThinking forces the selection to false. That regresses custom Anthropic endpoints that rely on adaptiveThinking to force adaptive thinking (the provider still forwards that alias field), so selecting the model from /model silently disables thinking instead of preserving/toggling it. Keep model.adaptiveThinking === true in the toggleable case.

Useful? React with 👍 / 👎.

this.opts = opts;
this.tabs = buildTabs(opts);

const preferredProvider = opts.initialTabId ?? opts.models[opts.currentValue]?.provider;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Select the tab for an explicit model alias

When /model <alias> names a model from a different provider than the current one, showModelPicker passes that alias as selectedValue, but the tabbed selector still opens the current provider's tab. That tab does not contain the requested alias, so pressing Enter confirms the current model instead of the alias the user typed; prefer opts.models[opts.selectedValue]?.provider before falling back to the current model provider.

Useful? React with 👍 / 👎.

Comment on lines +216 to +219
if (tokenValue.length === 0) {
this.hint = 'token-empty';
this.activeField = 'token';
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow tokenless custom registry imports

This dialog refuses to submit when the Bearer token field is empty, but fetchCustomRegistry explicitly supports an empty apiKey by omitting the Authorization header. As a result, users cannot import public/tokenless api.json registries through /provider even though the underlying importer can fetch them. Let an empty token submit as apiKey: '' instead of treating it as a validation error.

Useful? React with 👍 / 👎.

Comment on lines +47 to +48
name: 'provider',
aliases: ['providers'],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve /connect compatibility or mark a major bump

Replacing the /connect registry entry with /provider leaves no compatibility alias for existing users and scripts; after this change /connect ... is no longer recognized as a slash command and falls through as a normal message to the model. The repo rules treat removed commands as breaking changes, so either keep /connect as an alias/forwarder to the provider catalog flow or ship this with an explicit major-version change.

Useful? React with 👍 / 👎.

@sailist sailist force-pushed the haozhe/provider-tui branch from b2d7290 to 79938e5 Compare June 1, 2026 02:54
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79938e511e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

aliases: ['providers'],
description: 'Manage AI providers (add / delete / refresh)',
priority: 95,
availability: 'always',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make /provider idle-only during generation

When the assistant is streaming or compacting, availability: 'always' lets /provider open and perform add/delete actions even though those callbacks mutate provider config and can call refreshConfigAfterLogin() or clearActiveSessionAfterLogout() without a busy-state guard. In that state, adding a provider may call session.setModel() through the refresh path, and deleting the active provider can close the active session mid-turn; the old provider-mutating /connect command was idle-only by default. Keep /provider idle-only or add an internal streaming/compacting guard before allowing mutations.

Useful? React with 👍 / 👎.

Comment on lines +261 to +262
if (typeof output === 'number' && Number.isInteger(output) && output > 0) {
return output;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve output limits separately from context

When an imported registry model has limit.output but no limit.context, this uses the output-token cap as maxContextSize; for example an 8192 output limit makes the model look like it only has an 8k context window. The parsed output limit is also never written as maxOutputSize on the alias, so runtime provider setup cannot forward it as the model's max-token budget. Leave the context at the default when only output is present and store output in maxOutputSize instead.

Useful? React with 👍 / 👎.

initialTabId: providerId,
onSelect: ({ alias, thinking }) => {
host.restoreEditor();
void setDefaultModel(host, alias, thinking);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle failures while saving the picked default

After adding a catalog provider, this callback fires setDefaultModel() without any catch, but that helper writes config and calls refreshConfigAfterLogin(), which can reject if the config write fails or if activating the newly selected provider fails (for example because the entered API key is invalid). Since this selection happens after the slash-command handler has already returned, the dispatcher’s try/catch does not see the rejection, leaving the user with no error message and potentially an unhandled promise rejection; mirror performModelSwitch and surface the failure through host.showError.

Useful? React with 👍 / 👎.

…model selector

- add "/provider" slash command for managing AI providers with CRUD UI\n- add custom registry import via api.json URL and Bearer token\n- introduce tabbed model selector grouped by provider\n- add fetchCustomRegistry and applyCustomRegistryProvider in oauth package\n- replace deprecated "/connect" command with unified "/provider" flow\n- update provider and slash-command documentation
@sailist sailist force-pushed the haozhe/provider-tui branch from 79938e5 to ccb1060 Compare June 1, 2026 03:20
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccb10605c0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

aliases: [],
description: 'Switch LLM model',
priority: 100,
availability: 'always',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep /model blocked while compacting

When the app is compacting (isCompacting true while streamingPhase can still be idle), marking /model as always lets the picker open and then performModelSwitch proceeds because its only busy guard checks streamingPhase !== 'idle'; selecting a model can therefore call session.setModel/setThinking and persist defaults during compaction. Keep this command idle-only or add an isCompacting guard before applying the selection.

Useful? React with 👍 / 👎.

@sailist
Copy link
Copy Markdown
Collaborator Author

sailist commented Jun 1, 2026

@codex

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

'Run /login to sign in to Kimi, or /connect to add another provider from a model catalog.',

P3 Badge Replace stale /connect guidance after removing the command

This empty-model notice still tells users to run /connect, but the same change removes /connect from the command registry and dispatch path, so in this exact no-model state the suggested command is no longer executable and will be treated as normal input. Point users at /provider instead so they can actually add a provider from the TUI.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

let config = await host.harness.getConfig();
for (const entry of Object.values(entries)) {
if (config.providers[entry.id] !== undefined) {
config = await host.harness.removeProvider(entry.id);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve earlier imports when replacing registry providers

When re-importing a custom registry that contains multiple providers already present in the config, this call reloads the config from disk on each iteration. Any provider applied earlier in the loop has not been persisted yet, so a later removeProvider() overwrites config with a snapshot that does not include that earlier in-memory addition; after the final setConfig, only the providers applied after the last removal survive. Remove all conflicting providers before applying entries, or delete stale providers/models from the in-memory config instead of replacing config mid-loop.

Useful? React with 👍 / 👎.

description: 'Manage AI providers (add / delete / refresh)',
priority: 95,
availability: 'always',
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep provider management idle-only during active turns

Marking /provider as always lets users open it while a response is streaming or compaction is running, but the add/delete flows mutate the active runtime: picking a default after adding a provider goes through setDefaultModel()refreshConfigAfterLogin()activateModelAfterLogin(), which calls session.setModel() without the non-idle guard used by /model, and deleting the active provider can close the session. This can change or tear down the current session while a turn is still in flight, so provider mutations should stay idle-only or guard these actions explicitly.

Useful? React with 👍 / 👎.

aliases: [],
description: 'Switch LLM model',
priority: 100,
availability: 'always',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Keep /model idle-only until streaming switches work

With this availability change, /model is no longer blocked during streaming, so the user can spend time in the selector, but selecting a model still immediately hits performModelSwitch()'s streamingPhase !== 'idle' check and only shows Cannot switch models while streaming. Either keep the command idle-only or remove the later guard so the advertised always-available workflow can actually complete.

Useful? React with 👍 / 👎.

models: config.models,
});

await host.authFlow.refreshConfigAfterLogin();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh providers without reactivating the saved default

At this point the provider has only been added and the user has not picked the new default yet, but refreshConfigAfterLogin() also calls activateModelAfterLogin() for whatever config.defaultModel currently says. If the current session is using a runtime-selected model that differs from the saved default, simply adding a provider can silently switch the session back before the selector appears (and canceling the selector leaves it there); use a model/provider snapshot refresh here and only activate after setDefaultModel().

Useful? React with 👍 / 👎.

const spinner = host.showLoginProgressSpinner(`Fetching catalog from ${DEFAULT_CATALOG_URL}`);
let catalog: Catalog | undefined;
try {
catalog = await fetchCatalog(DEFAULT_CATALOG_URL, controller.signal);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the built-in catalog fallback for provider add

The new known-provider path always fetches DEFAULT_CATALOG_URL and returns on failure, while the removed /connect path first used the bundled catalog and could fall back to it when the network was unavailable. Since /connect is no longer reachable, users behind a proxy or offline can no longer add catalog providers even though the built-in catalog is still shipped; load BUILT_IN_CATALOG_JSON as the default/fallback here or keep an equivalent command path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7ed23e0ed

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

let changedAny = false;

for (const providerId of providerIds) {
const entry = entries[providerId];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match custom registry entries by entry id

For custom registries whose top-level object key differs from the provider id, this lookup never finds the refreshed entry: fetchCustomRegistry returns a record keyed by the top-level key, while imported providers are stored under entry.id (the parser comment also notes these may differ). In that case startup/background refresh silently skips the provider, so model additions/removals from that registry are never applied; iterate the fetched values and match entry.id to providerId instead.

Useful? React with 👍 / 👎.

applyOpenPlatformConfig(asManaged(config), {
platform,
models,
selectedModel,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve saved defaults during platform refresh

When a non-default Open Platform provider has a model-list change during the startup refresh, pickDefaultModel() falls back to that provider's first model unless the existing default already belongs to the same provider, and applyOpenPlatformConfig() then writes that alias into config.defaultModel. Because this patch persists defaultModel, merely refreshing an unrelated Moonshot Open Platform account can replace the user's saved default model for the next launch; preserve the previous default unless it pointed at one of the refreshed provider's removed aliases.

Useful? React with 👍 / 👎.

activeProviderId,
colors: host.state.theme.colors,
onAdd: () => {
void handleProviderAdd(host);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report failures from the add-provider callback

After the manager is already mounted, pressing the add row runs handleProviderAdd() as a fire-and-forget promise, so the slash-command dispatcher's try/catch no longer observes failures. If the catalog add path later rejects while writing config or refreshing the newly added provider, the user gets no host.showError() and the rejection can become unhandled; catch this promise here the way deletion errors are handled.

Useful? React with 👍 / 👎.

unchanged.push(KIMI_CODE_PROVIDER_NAME);
} else {
const { added, removed } = computeChanges(beforeIds, newIds);
config = await host.removeProvider(KIMI_CODE_PROVIDER_NAME);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the selected Kimi model during refresh

When the managed Kimi model list changes, removeProvider() deletes all Kimi aliases and clears config.defaultModel if it pointed at one of them before applyManagedKimiCodeConfig(..., preserveDefaultModel: true) runs. That makes the preserve flag ineffective, so a user whose saved default is still available can be reset to the first returned Kimi model on the next startup refresh; keep the previous default alias before removing the provider and restore it when it is still in the refreshed list.

Useful? React with 👍 / 👎.

const beforeIds = collectModelIdsForProvider(config, providerId);
const newIds = new Set(models.map((m) => m.id));

if (setsEqual(beforeIds, newIds)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh model metadata when ids are unchanged

For an Open Platform refresh where /models returns the same model IDs but updated metadata such as context_length, display name, or reasoning/tool support, this branch marks the provider unchanged and skips rewriting the aliases. The TUI/runtime will then keep stale maxContextSize and capabilities (for example using an old compaction window) until a model is added or removed; compare the alias metadata or reapply the provider config even when the id set is equal.

Useful? React with 👍 / 👎.

@sailist sailist force-pushed the haozhe/provider-tui branch from b7ed23e to 784253b Compare June 1, 2026 07:45
- add `refreshAllProviderModels` utility supporting managed OAuth, open platforms and custom registries\n- wire background refresh into `KimiTUI` startup via `AuthFlowController`\n- add `providerSwitchHint` option to `ModelSelector` and enable it in `TabbedModelSelector`\n- update `TabbedModelSelector` hint wording from "switch" to "provider"
@sailist sailist force-pushed the haozhe/provider-tui branch from 784253b to f9c847b Compare June 1, 2026 07:49
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9c847ba40

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +257 to +258
if (setsEqual(beforeIds, newIds)) {
unchanged.push(providerId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh custom registry metadata when IDs are unchanged

When a custom registry returns the same model IDs but changes metadata such as limit.context, capabilities, display name, or the provider api URL, this branch records the provider as unchanged and skips applyCustomRegistryProvider(). The startup refresh then leaves stale aliases/provider config in place, so the TUI/runtime can keep using an old context window or endpoint until a model is added or removed; reapply the entry or compare the full alias/provider metadata instead of only the ID set.

Useful? React with 👍 / 👎.

Comment on lines +250 to +252
for (const providerId of providerIds) {
const entry = entries[providerId];
if (entry === undefined) continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Synchronize provider additions and removals from registries

When the api.json source adds a new top-level provider or removes one that was previously imported, this refresh loop only visits the provider IDs already in config and silently skips missing entries. That leaves removed providers selectable forever and never imports newly published providers from the same registry source during startup refresh; compare the fetched entry set with providerIds and apply/remove providers at the source level instead of continuing here.

Useful? React with 👍 / 👎.

Comment on lines +289 to +293
config.providers[providerKey] = {
type: entry.type,
baseUrl: entry.api,
apiKey: source.apiKey,
source,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not reuse the registry token as the provider API key

For custom registries where the api.json fetch token is not the same credential used by the model endpoint (including public registries with provider credentials named in entry.env), this writes source.apiKey into the provider config and drops the parsed entry.env. The runtime later resolves provider auth from provider.apiKey/standard env keys, so imported models can send the registry bearer token—or no token at all—to the provider API; preserve the provider credential source separately instead of copying the registry fetch token here.

Useful? React with 👍 / 👎.

Comment on lines +261 to +263
config = await host.removeProvider(providerId);
removeCustomRegistryProvider(asManaged(config), providerId);
applyCustomRegistryProvider(asManaged(config), entry, source);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve custom registry defaults during refresh

When a custom-registry provider's model set changes, this call clears config.defaultModel on disk whenever the saved default belongs to that provider, even if that alias is still present in the refreshed registry. applyCustomRegistryProvider() does not restore a default and the later setConfig() omits defaultModel, so adding or removing an unrelated model from the registry can silently drop the user's saved default for the next launch; keep the previous default when it still exists after reapplying the entry.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant